import { _decorator, color, Component, Graphics, instantiate, Label, Node, NodePool, Rect, Tween, tween, UITransform, v3, Vec3 } from 'cc';
const { ccclass, property } = _decorator;

/** 引导的完成类型（指示引导是如何被完成的） */
enum DoneType {
	/** 点击完成 */
	TOUCH,
	/** 延时完成 */
	DELAY,
	/** 以前完成过的  */
	DONE,
}

@ccclass('Guide')
export class Guide extends Component {

	@property(Graphics)
	private graphics: Graphics = null;
	@property(UITransform)
	private ui_toucher: UITransform;
	@property(UITransform)
	private ui_pointer: UITransform = null;
	@property(UITransform)
	private ui_content: UITransform = null;
	@property(Label)
	private lbl_content: Label = null;
	@property(UITransform)
	private ui_hilight_container: UITransform = null;

	private uitrans: UITransform;
	private ui_graphics: UITransform;

	private va_content: mtec.cc.VecAssist;

	public static DoneType = DoneType;

    protected start() {
		if([1, 2, 3, 4, 5, 6, 7].every(id=>GameDC.checkGuideID(id))){
			this.node.removeFromParent();
			this.node.destroy();
		}else{
			Guide.ins = this;
			this.init();
		}
		Guide.__guide_inited__.resolve('end');
    }

	private init(){
		this.uitrans = this.node.getComponent(UITransform);
		this.ui_graphics = this.graphics.node.getComponent(UITransform);
		this.va_content = new mtec.cc.VecAssist(this.ui_content.node);
		this.ui_toucher.node.on(Node.EventType.TOUCH_END, this.touchEnd);

		this.touch_info.world_position.set(0, 0, this.ui_toucher.node.position.z);
		this.touch_info.local_position.set(this.ui_toucher.node.position.clone());
		this.point_info.world_position.set(0, 0, this.ui_pointer.node.position.z);
		this.point_info.local_position.set(this.ui_pointer.node.position.clone());

		this.node.active = false;
	}

	private content_local = v3();
	public showContent(content: string, rect: Rect){
		this.ui_content.node.active = true;
		this.lbl_content.string = content;
		this.ui_content.convertToNodeSpaceAR(world_pos, this.content_local);

		if(this.content_local.x < 0){
			this.content_local.x = Math.max(this.content_local.x, this.va_content.left);
		}else if(this.content_local.x > 0){
			this.content_local.x = Math.min(this.content_local.x, this.va_content.right);
		}

		if(this.content_local.y < 0){
			this.content_local.y = Math.max(this.content_local.y, this.va_content.bottom);
		}else if(this.content_local.y > 0){
			this.content_local.y = Math.min(this.content_local.y, this.va_content.top);
		}

		this.content_local.z = this.ui_content.node.position.z;

		Tween.stopAllByTarget(this.ui_content.node);
		tween(this.ui_content.node)
		.to(0.25, {position: this.content_local})
		.start();
	}
	public hideContent(){
		this.ui_content.node.active = false;
		this.lbl_content.string = '';
	}

	private light_info = {
		world_position: v3(),
		local_position: v3(),
		clear_color: color(0, 0, 0, 120),
		light_color: color(0, 0, 0, 0),
	};
	/**
	 * 显示高亮区域
	 * @param list
	 */
	public showLight(...list: Rect[]){
		this.graphics.node.active = true;
		this.graphics.clear();
		this.graphics.fillColor = this.light_info.clear_color;

		this.graphics.rect(
			this.ui_graphics.width * -0.5,
			this.ui_graphics.height * -0.5,
			this.ui_graphics.width,
			this.ui_graphics.height
		);
		this.graphics.fill();

		if(!(list.length>0)){
			return void 0;
		}

		this.graphics.fillColor = this.light_info.light_color;

		list.forEach(rt=>{
			this.light_info.world_position.set(rt.x, rt.y, this.graphics.node.position.z);
			this.ui_graphics.convertToNodeSpaceAR(this.light_info.world_position, this.light_info.local_position);

			this.graphics.rect(
				this.light_info.local_position.x,
				this.light_info.local_position.y,
				rt.width, rt.height
			);

			this.graphics.fill();
		});
	}
	public cleanLight(){
		this.graphics.clear();
		this.graphics.node.active = false;
	}

	private point_info = {
		world_position: v3(),
		local_position: v3(),
	};
	/**
	 * 显示引导动画(小手的动画)
	 * @param target
	 */
	public showPointer(world_position: Vec3){
		this.uitrans.convertToNodeSpaceAR(world_position, this.point_info.local_position);

		Tween.stopAllByTarget(this.ui_pointer.node);
		this.ui_pointer.node.active = true;
		this.ui_pointer.node.setPosition(this.point_info.local_position);

		let ratio = 1 / 3;
		let distance1 = v3(this.ui_pointer.width * ratio, this.ui_pointer.height * -ratio, 0);
		let distance2 = v3(this.ui_pointer.width * -ratio, this.ui_pointer.height * ratio, 0);
		tween(this.ui_pointer.node)
		.by(0.15, {position: distance1})
		.by(0.15, {position: distance2})
		.union().repeat(3).delay(0.8)
		.union().repeatForever().start();
	}
	public hidePointer(){
		Tween.stopAllByTarget(this.ui_pointer.node);
		this.ui_pointer.node.active = false;
	}

	private touch_info = {
		world_position: v3(),
		local_position: v3(),
	};
	public refreshToucher(rt: Rect){
		this.ui_toucher.node.active = true;
		this.touch_info.world_position.x = rt.x + rt.width * 0.5;
		this.touch_info.world_position.y = rt.y + rt.height * 0.5;

		this.uitrans.convertToNodeSpaceAR(this.touch_info.world_position, this.touch_info.local_position);

		this.ui_toucher.setContentSize(rt.width, rt.height);
		this.ui_toucher.node.setPosition(this.touch_info.local_position);
	}
	public cleanToucher(){
		this.touch_info.world_position.set(0, 0);
		this.uitrans.convertToNodeSpaceAR(this.touch_info.world_position, this.touch_info.local_position);

		this.ui_toucher.setContentSize(0, 0);
		this.ui_toucher.node.setPosition(this.touch_info.local_position);
		this.ui_toucher.node.active = false;
	}

	private hilit_info = {
		world_position: v3(),
		local_position: v3(),
		pool_map: new Map<Node, NodePool>(),
		live_cache: new Map<Node, Node[]>(),
	}
	public hilight(list: UITransform[]){
		let canvas = mtec.cc.canvas;
		let base_index = this.ui_hilight_container.node.children.length;
		list.map(ui=>{
			let depth: number[] = [];
			let r = ui.node;
			while(r != canvas.node){
				depth.unshift(r.getSiblingIndex());
				r = r.parent;
			}
			return {depth, ui};
		}).sort((a, b)=>{
			let i = 0;
			while(a.depth[i]==b.depth[i]) i++;
			return a.depth[i] - b.depth[i];
		}).forEach((el, i)=>{
			let pool = this.hilit_info.pool_map.get(el.ui.node);
			if(!pool){
				pool = new NodePool();
				this.hilit_info.pool_map.set(el.ui.node, pool);
			}

			let demo = pool.get() ?? instantiate(el.ui.node);

			let cache = this.hilit_info.live_cache.get(el.ui.node);
			if(!cache){
				cache = [];
				this.hilit_info.live_cache.set(el.ui.node, cache);
			}
			cache.push(demo);

			demo.setParent(this.ui_hilight_container.node);
			demo.setSiblingIndex(base_index + i);
			this.ui_hilight_container.convertToNodeSpaceAR(el.ui.node.worldPosition, this.hilit_info.local_position);
			demo.setPosition(this.hilit_info.local_position);
		});
	}
	public releaseHilight(list: Array<UITransform|Node>){
		list.forEach(el=>{
			let node: Node;
			if(el instanceof UITransform){
				node = el.node;
			}else{
				node = el;
			}

			if(!this.hilit_info.pool_map.has(node)){
				return void 0;
			}

			let pool = this.hilit_info.pool_map.get(node);
			if(!pool){
				pool = new NodePool();
				this.hilit_info.pool_map.set(node, pool);
			}
			this.hilit_info.live_cache.get(node).forEach(el=>pool.put(el));
		});
	}

	private close(){
		Tween.stopAllByTarget(this.ui_pointer.node);
		this.node.active = false;
	}

	private touchEnd(){
		Guide.onTouch();
	}

	public setShowID(id: number){
		Guide.show_guideID = id;
		this.node.active = true;
		this.hideContent();
		this.cleanLight();
		this.hidePointer();
		this.cleanToucher();

		if(this.hilit_info.live_cache.size>0){
			this.hilit_info.live_cache.forEach((list, node)=>{
				let pool = this.hilit_info.pool_map.get(node);
				if(!pool){
					pool = new NodePool();
					this.hilit_info.pool_map.set(node, pool);
				}
				list.forEach(n=> pool.put(n));
			});
			this.hilit_info.live_cache.clear();
		}
	}

	private static ins: Guide;
	private static show_guideID: number;
	private static done_record: number[];
	private static map_action: Map<number, [GuideAction, mtec.NudityPromise<DoneType>]>;
	private static call_touch: Map<number, Function[]>;

	private static __guide_inited__ = new mtec.NudityPromise<'end'>();
	public static get guideInited(){
		return Guide.__guide_inited__.promise;
	}
	public static get showID(){
		return Guide.show_guideID;
	}

	private static onTouch(){
		if(Guide.call_touch.has(Guide.show_guideID)){
			Guide.call_touch.get(Guide.show_guideID).forEach(callback=>callback());
		}
	}

	private static cleanGuideInfo(id: number){
		if(Guide.map_action.has(id)){
			let [a, n] = Guide.map_action.get(id);
			n.resolve(DoneType.DONE);
			Guide.map_action.delete(id);
		}

		if(Guide.call_touch.has(id)){
			Guide.call_touch.delete(id);
		}
	}

	public static init(){
		Guide.__guide_inited__ = new mtec.NudityPromise<'end'>();

		let record = mtec.local.read<string>(APP_NAME + 'guide_record');
		if(record){
			Guide.done_record = record.split(';').map(el=>parseInt(el));
		}else{
			this.done_record = [];
		}

		Guide.map_action = new Map();
		Guide.call_touch = new Map();
	}

	public static create(id: number){
		if(Guide.checkGuideID(id)){
			mtec.log.warn('Guide.create: 该引导已经完成过了');
			return void 0;
		}

		let action = new GuideAction(Guide.ins, id);
		Guide.map_action.set(id, [action, new mtec.NudityPromise()]);
		return action;
	}

	public static register_touch(id: number, callback: Function){
		if(!Guide.call_touch.has(id)){
			Guide.call_touch.set(id, []);
		}
		Guide.call_touch.get(id).push(callback);
	}

	private static save_record(){
		mtec.local.save(APP_NAME + 'guide_record', Guide.done_record.join(';'));
	}
	public static checkGuideID(id: number){
		return Guide.done_record.includes(id);
	}
	public static cleanGuideID(...list: number[]){
		list.forEach(id=>{
			mtec.array.remove(Guide.done_record, id);
			Guide.cleanGuideInfo(id);
		});
		Guide.save_record();
	}

	public static wait(id: number){
		let prms: Promise<DoneType>;
		if(Guide.map_action.has(id)){
			prms = Guide.map_action.get(id)[1].promise;
		}else if(Guide.checkGuideID(id)){
			prms = Promise.resolve(DoneType.DONE);
		}else{
			Guide.create(id);
			prms = Guide.map_action.get(id)[1].promise;
		}

		return prms;
	}

	public static show(id: number){
		if(Guide.show_guideID != undefined){
			mtec.log.warn('Guide.show: 有一个引导正在显示中');
			return void 0;
		}

		if(!Guide.map_action.has(id)){
			mtec.log.warn('Guide.show: 未找到指定的引导ID');
			return void 0;
		}

		let [action, np] = Guide.map_action.get(id);
		action.show();
		return np.promise;
	}

	public static done(id: number, type: DoneType){
		if(Guide.map_action.has(id)){
			let [action, np] = Guide.map_action.get(id);
			np.resolve(type);
			Guide.done_record.push(id);
			Guide.save_record();
			Guide.show_guideID = undefined;
			Guide.cleanGuideInfo(id);
		}
	}
}

class GuideAction {
	private str_content: string;
	private ui_content: UITransform|Vec3;
	private target_pointer_pos: Vec3;
	private ui_light: Array<Rect>;
	private hilight_list: Array<Node>;
	private rect_toucher: Rect;
	private mark_enable: boolean;

	private guide_comp: Guide;
	private id: number;

	constructor(guide_comp: Guide, id: number){
		this.guide_comp = guide_comp;
		this.id = id;
		this.mark_enable = true;
	}

	public content(str: string, uitrans: UITransform|Vec3){
		this.str_content = str;
		this.ui_content = uitrans;
		return this;
	}

	public pointer(uitrans: UITransform|Vec3){
		if(uitrans instanceof UITransform){
			this.target_pointer_pos = uitrans.node.worldPosition;
		}else{
			this.target_pointer_pos = uitrans;
		}
		return this;
	}

	public light(list: UITransform[]){
		this.ui_light = list.map(ui=>{
			let rt = new Rect();
			rt.width = ui.width * ui.node.scale.x;
			rt.height = ui.height * ui.node.scale.y;
			rt.x = ui.node.worldPosition.x - ui.anchorX * rt.width;
			rt.y = ui.node.worldPosition.y - ui.anchorY * rt.height
			return rt;
		});
		this.mark_enable = true;
		return this;
	}

	public hilight(list: Node[]){
		this.hilight_list = list;
		this.mark_enable = true;
		return this;
	}

	public enableMark(enable: boolean){
		this.mark_enable = enable;
		if(this.hilight_list.length>0 || this.ui_light.length>0){
			this.mark_enable = true;
		}
		return this;
	}

	public toucher(uitrans: UITransform|Rect, callback: ()=>void){
		if(uitrans instanceof UITransform){
			this.rect_toucher = new Rect();
			this.rect_toucher.width = uitrans.width * uitrans.node.scale.x;
			this.rect_toucher.height = uitrans.height * uitrans.node.scale.y;
			this.rect_toucher.x = uitrans.node.worldPosition.x - uitrans.anchorX * this.rect_toucher.width;
			this.rect_toucher.y = uitrans.node.worldPosition.y - uitrans.anchorY * this.rect_toucher.height;
		}else{
			this.rect_toucher = uitrans;
		}
		Guide.register_touch(this.id, callback);
		return this;
	}

	public async show(){
		await Guide.guideInited;

		if(Guide.showID!=undefined){
			mtec.log.warn('GuideAction.show: 有一个引导正在显示中');
			return void 0;
		}

		this.guide_comp.setShowID(this.id);
		if(this.str_content){
			this.guide_comp.showContent(this.str_content, this.ui_content);
		}
	}

	public wait(id: number){

	}
}
